Skip to content

feat: typed error handling, DeleteSession, health check improvements#1

Merged
sakost merged 13 commits intomasterfrom
feat/error-handling-and-improvements
Feb 21, 2026
Merged

feat: typed error handling, DeleteSession, health check improvements#1
sakost merged 13 commits intomasterfrom
feat/error-handling-and-improvements

Conversation

@sakost
Copy link
Copy Markdown
Owner

@sakost sakost commented Feb 18, 2026

Summary

  • Typed error handling: Sealed AppException hierarchy with ErrorMappingInterceptor that converts raw GrpcError into human-readable typed exceptions. Global ConnectivityBanner for persistent network/relay status. All UI error displays now show friendly messages instead of raw gRPC errors.
  • DeleteSession RPC: Full implementation with confirmation dialog, cache cleanup, and tests.
  • Health check on resume: GrpcLifecycleBridge verifies channel health after short backgrounds to detect zombie TCP connections. Catches UNIMPLEMENTED as defense-in-depth.
  • Proto update: Submodule updated with DeleteSession, SubagentService, NotificationService, command registry fields.

Commits

  1. Proto update + DeleteSession — regenerated code, delete flow with confirmation, notification plan doc
  2. Health check improvements — zombie channel detection on app resume, UNIMPLEMENTED tolerance
  3. Typed error handling — AppException hierarchy, ErrorMappingInterceptor, ConnectivityBanner, updated all error display paths

Test plan

  • 1033 tests pass (flutter test)
  • dart analyze lib/ test/ — zero issues
  • Kill relay → orange "Relay unreachable" banner, no logout
  • Airplane mode → red "No internet" banner
  • Restore network → banner dismisses
  • Delete session → confirmation dialog → session removed
  • Open deleted session → friendly error, not raw gRPC

🤖 Generated with Claude Code

sakost and others added 13 commits February 18, 2026 17:18
…ervice

- Update proto submodule to latest (DeleteSession RPC, command registry
  group/displayName/sessionId fields, SubagentService, NotificationService)
- Regenerate all Dart protobuf code
- Implement deleteSession() in SessionsNotifier with cache cleanup
- Add ConfirmDeleteDialog widget with destructive styling
- Wire delete flow in SessionsScreen with confirmation
- Add deleteSession tests
- Set default model in StartConversation to prevent relay "default" fallback
- Update command palette for new proto fields
- Add notification service plan doc

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add health check verification in GrpcLifecycleBridge.onResumed() to
  detect zombie channels after short backgrounds (< 5 min)
- Catch UNIMPLEMENTED in healthCheckFn as defense-in-depth (proves
  connection is alive even if relay lacks betcode.v1.Health)
- Add hasHealthCheck and healthCheck() to GrpcClientManager
- Add tests for health check scenarios (failing, UNIMPLEMENTED, no check)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ty banner

- Add sealed AppException class with 8 typed subclasses (NetworkError,
  RelayUnavailableError, SessionNotFoundError, SessionInvalidError,
  AuthExpiredError, PermissionDeniedError, ServerError, RateLimitError)
- Add mapGrpcError() function mapping gRPC status codes to AppExceptions
  with human-readable messages and context-sensitive session detection
- Add ErrorMappingInterceptor as last in gRPC interceptor chain
- Add global ConnectivityBanner widget in app.dart showing persistent
  network/relay status (red for offline, orange for relay unreachable)
- Update ConversationNotifier to use typed exceptions for fatal error
  detection and human-readable error messages
- Update SessionsScreen to catch typed exceptions with friendly messages
  and handle SessionNotFoundError on delete gracefully
- Update ErrorDisplay widget to show AppException.message instead of
  raw toString()
- Update AuthNotifier._isAuthError() to recognize typed exceptions
- Add comprehensive tests (35 new test cases)
- Add error handling plan doc

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When resuming a session whose history contained a fatal error (e.g.
"Claude exited with error"), the event handler treated replayed errors
as live ones, transitioning to ConversationError and making the session
permanently unrecoverable. Also fixes stream leaks on retry.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…gging

- Service providers now watch connectionStatusProvider so clients rebuild
  when the channel is replaced during reconnection (fixes rapid-fire
  "Unable to reach relay" errors from stale channel references)
- Separate "Channel shutting down" from TLS errors in error mapping so
  transient reconnection errors don't show "relay unreachable" banner
- Handle StatusCode.cancelled as retryable NetworkError instead of
  falling through to "Something went wrong" catch-all
- LoggingInterceptor now logs streaming errors and completion (was silent)
- ErrorMappingInterceptor logs original GrpcError code+message before
  mapping, and _handleStreamError logs the cause for diagnostics

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
container.read(machinesProvider.future) never resolved on a bare
ProviderContainer before runApp(), even though the AsyncNotifier
build completed successfully. Replace with container.listen +
Completer pattern that reliably awaits async provider state
transitions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix failing tests: error_mapping (Channel shutting down -> NetworkError)
  and interceptors (LoggingInterceptor wraps stream)
- Fix _loadHistory catching AppException in addition to GrpcError so
  error-mapped exceptions show soft banner instead of killing conversation
- Move ConnectivityBanner inside MaterialApp.router builder for proper
  theme/directionality context
- Make SessionNotFoundError.sessionId optional (was always empty string)
- Remove duplicate debugPrint in ErrorMappingResponseStream
- Remove redundant imports in ConversationEventHandler
- Document best-effort health check design decision in GrpcClientManager
- Add tests for optional sessionId and AppException history load path

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add CI workflow with analyze, test, build-android, and build-ios jobs.
Migrate iOS from AppDelegate-based window management to UIScene with
SceneDelegate. Bump iOS deployment target from 13.0 to 16.0.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@sakost sakost merged commit a524fe5 into master Feb 21, 2026
4 checks passed
@sakost sakost deleted the feat/error-handling-and-improvements branch February 21, 2026 11:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant